//
//  YLDotPageControl.swift
//  GLUZSwift
//
//  Created by xyanl on 2025/5/9.
//

import UIKit
import SnapKit

/**
 自定义分页指示器控件，支持圆点大小动画效果
 
 功能特性：
 - 可自定义普通圆点和选中圆点的大小、颜色
 - 平滑的切换动画效果
 - 精确的自动布局计算
 - 支持点击切换页码
 */
class YLDotPageControl: UIView {
    
    // MARK: - 公开可配置属性
    
    /// 总页数，设置时会重新创建所有圆点
    @IBInspectable var numberOfPages: Int = 0 {
        didSet {
            // 只有当页数真正改变时才更新UI
            guard numberOfPages != oldValue else { return }
            
            // 重新创建圆点
            setupDots()
            
            // 通知系统重新计算固有内容大小
            invalidateIntrinsicContentSize()
        }
    }
    
    /// 当前页码，设置时会触发选中状态动画
    @IBInspectable var currentPage: Int = 0 {
        didSet {
            // 确保页码确实发生了变化
            guard currentPage != oldValue else { return }
            
            // 更新圆点状态
            updateDots()
        }
    }
    
    /// 普通圆点的直径大小（默认8pt）
    @IBInspectable var dotSize: CGFloat = 8 {
        didSet {
            // 确保普通圆点大小不超过选中圆点大小
            dotSize = min(dotSize, activeDotSize)
            
            // 更新圆点显示
            updateDots()
            
            // 通知系统重新计算布局
            invalidateIntrinsicContentSize()
        }
    }
    
    /// 选中圆点的直径大小（默认16pt）
    @IBInspectable var activeDotSize: CGFloat = 16 {
        didSet {
            // 确保选中圆点不小于普通圆点
            activeDotSize = max(activeDotSize, dotSize)
            
            // 更新圆点显示
            updateDots()
            
            // 通知系统重新计算布局
            invalidateIntrinsicContentSize()
        }
    }
    
    /// 普通圆点的颜色（默认浅灰色）
    @IBInspectable var dotColor: UIColor = .lightGray {
        didSet {
            // 更新圆点显示
            updateDots()
        }
    }
    
    /// 选中圆点的颜色（默认系统蓝色）
    @IBInspectable var activeDotColor: UIColor = .systemBlue {
        didSet {
            // 更新圆点显示
            updateDots()
        }
    }
    
    /// 圆点之间的间距（默认10pt）
    @IBInspectable var spacing: CGFloat = 10 {
        didSet {
            // 更新堆栈视图的间距
            stackView.spacing = spacing
            
            // 通知系统重新计算布局
            invalidateIntrinsicContentSize()
        }
    }
    
    // MARK: - 私有属性
    
    /// 用于布局圆点的堆栈视图
    private lazy var stackView: UIStackView = {
        let stack = UIStackView()
        stack.axis = .horizontal  // 水平排列
        stack.alignment = .center // 垂直方向居中对齐
        stack.distribution = .fill // 均匀分布
        stack.spacing = spacing   // 使用配置的间距
        return stack
    }()
    
    /// 存储所有圆点视图的数组
    private var dots: [UIView] = []
    
    /// 存储所有圆点的尺寸约束（宽度和高度）
    /// 每个圆点对应两个约束：[宽度约束, 高度约束]
    private var dotConstraints: [UIView: [NSLayoutConstraint]] = [:]
    
    // MARK: - 初始化方法
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        // 初始化视图层次结构
        setupViews()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        // 初始化视图层次结构
        setupViews()
    }
    
    // MARK: - 视图生命周期
    
    /// 设置视图层次结构
    private func setupViews() {
        // 添加堆栈视图
        addSubview(stackView)
        
        // 使用SnapKit设置约束，使堆栈视图填满整个控件
        stackView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
    }
    
    // MARK: - 圆点管理
    
    /// 创建并设置所有圆点
    private func setupDots() {
        // 移除现有的所有圆点
        stackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
        dots.removeAll()
        dotConstraints.removeAll()
        
        // 创建新的圆点
        for _ in 0..<numberOfPages {
            let dot = UIView()
            dot.backgroundColor = dotColor
            dot.layer.masksToBounds = true // 确保圆角效果不会超出边界
            dot.layer.cornerRadius = dotSize / 2 // 初始圆角半径
            
            // 添加到堆栈视图和数组
            stackView.addArrangedSubview(dot)
            dots.append(dot)
            
            // 使用SnapKit设置基础约束
            dot.snp.makeConstraints { make in
                make.width.equalTo(dotSize)
                make.height.equalTo(dotSize)
            }
            
            // 获取并存储原生约束以便精确控制
            var constraints: [NSLayoutConstraint] = []
            if let widthConstraint = dot.constraints.first(where: { $0.firstAttribute == .width }) {
                constraints.append(widthConstraint)
            }
            if let heightConstraint = dot.constraints.first(where: { $0.firstAttribute == .height }) {
                constraints.append(heightConstraint)
            }
            dotConstraints[dot] = constraints
        }
        
        // 初始更新圆点状态（不带动画）
        updateDots(animated: false)
    }
    
    /**
     更新所有圆点的状态（大小和颜色）
     
     - Parameter animated: 是否显示动画效果，默认为true
     */
    private func updateDots(animated: Bool = true) {
        // 遍历所有圆点
        for (index, dot) in dots.enumerated() {
            // 判断当前圆点是否选中状态
            let isActive = index == currentPage
            
            // 确定目标大小和颜色
            let targetSize = isActive ? activeDotSize : dotSize
            let targetColor = isActive ? activeDotColor : dotColor
            
            // 更新圆角半径（始终为当前尺寸的一半）
            dot.layer.cornerRadius = targetSize / 2
            
            // 获取并更新约束
            if let constraints = dotConstraints[dot] {
                constraints.forEach { $0.constant = targetSize }
            }
            
            // 定义更新UI的闭包
            let animations = {
                dot.backgroundColor = targetColor
                self.layoutIfNeeded() // 强制立即应用约束变化
            }
            
            // 根据是否需要动画选择执行方式
            if animated {
                // 使用弹簧动画使过渡更自然
                UIView.animate(
                    withDuration: 0.5,
                    delay: 0,
                    usingSpringWithDamping: 0.7, // 阻尼系数（0-1，越小弹性越大）
                    initialSpringVelocity: 0.5,  // 初始速度
                    options: .curveEaseOut,      // 动画曲线
                    animations: animations,
                    completion: nil
                )
            } else {
                animations()
            }
        }
    }
    
    // MARK: - 布局计算
    
    /**
     返回控件的固有内容大小（自动布局使用）
     
     系统在自动布局时会调用此方法获取控件的理想大小
     */
    override var intrinsicContentSize: CGSize {
        // 没有圆点时返回零大小
        guard numberOfPages > 0 else { return .zero }
        
        // 计算总宽度：
        // 1. 普通圆点的总宽度 = (总页数-1) * 普通圆点大小
        // 2. 加上选中圆点的宽度
        // 3. 加上所有间距
        let normalDotsCount = max(0, numberOfPages - 1)
        let normalDotsWidth = CGFloat(normalDotsCount) * dotSize
        let activeDotWidth = activeDotSize
        let totalSpacing = CGFloat(normalDotsCount) * spacing
        
        // 高度始终使用选中圆点的大小，确保有足够空间显示
        return CGSize(
            width: normalDotsWidth + activeDotWidth + totalSpacing,
            height: activeDotSize
        )
    }
    
    // MARK: - 用户交互
    
    /**
     处理触摸结束事件，实现点击切换页码
     
     当用户点击某个圆点时，会将currentPage设置为对应的索引
     */
    //    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    //        guard let touch = touches.first else { return }
    //        
    //        // 获取触摸点在当前视图中的位置
    //        let location = touch.location(in: self)
    //        
    //        // 检查触摸点是否在某个圆点上
    //        for (index, dot) in dots.enumerated() {
    //            // 将圆点的bounds转换为当前视图坐标系
    //            let dotFrame = dot.convert(dot.bounds, to: self)
    //            
    //            // 如果触摸点在圆点范围内，切换到对应页码
    //            if dotFrame.contains(location) {
    //                currentPage = index
    //                xLog("切换 \(index)")
    //                break
    //            }
    //        }
    //    }
}
